package main

import (
	"fmt"
	"sync"
)

// 获取协程等待
var syncs sync.WaitGroup

/*
   goroutine
   协程的概念看process吧
   说一句 协程更轻量 是用户级别的 效率更高。
   并行是多核cpu同时进行 并发是一个cpu切换上下文一个时间只有一个在跑
   必须有主线程来开协程，主线线程不会等协程 主线程结束不管协程是否结束都结束
   开启携程大概2kb 其它语言类似java线程大概2m左右 内存

   语法
*/

// func sum(len int) int64 {
// 	var sum int64
// 	for i := 0; i < len; i++ {
// 		if i%3 == 0 {
// 			sum++
// 		}
// 	}
// 	fmt.Println(sum)
// 	syncs.Done() // 协程队列-1
// 	return sum
// }

// func prints(start, end int) {
// 	defer syncs.Done()
// 	for i := start; i < end; i++ {
// 		if i%3 == 0 {
// 			fmt.Print(i)
// 		}
// 	}
// }

func main() {
	// // fmt.Pri/ntln(sum(10000000000))
	// syncs.Add(1)  // 添加一个协程+1
	// go sum(10000) // 开启协程
	// syncs.Wait()  // 主线程等待协程序 Add后列表0 都完成， 开始添加1 结束减1

	// 获取cpu核数 默认用满
	// fmt.Print(runtime.NumCPU())
	// // 设置cpu核
	// runtime.GOMAXPROCS(runtime.NumCPU())

	// starttime := time.Now().UnixNano()
	// for i := 0; i < 5; i++ {
	// 	syncs.Add(1)
	// 	go prints(i*1000, (i+1)*1000)
	// }
	// syncs.Wait()
	// endtime := time.Now().UnixNano()
	// fmt.Print("---->", endtime-starttime)

	// 协程间数据传递 channel 管道 引用类型需要make申请空间 大于申请空间或者小于0 报错溢出
	// ch := make(chan int, 4)
	// // 写入
	// ch <- 1
	// // 读取
	// fmt.Println(<-ch) // <-ch 直接取或者 a := <-ch

	// 打印管道的成都和容量
	// ch := make(chan int, 4)
	// fmt.Println(len(ch), cap(ch)) // len 使用了多少个 cap容量上限多少个

	// 管道是引用类型

	// 管道的阻塞 写入到上限或者读取没了都触发阻塞

	// 管道的循环遍历 读写是互斥的 安全的无论哪个快另一个都在等待 不会阻塞
	// ch := make(chan int, 10)
	// syncs.Add(1)
	// go func() {
	// 	defer func() {
	// 		close(ch)
	// 		syncs.Done()
	// 	}()
	// 	for i := 0; i < 16; i++ {
	// 		ch <- i
	// 	}
	// }()

	// // 读取
	// syncs.Add(1)
	// go func() {
	// 	defer func() {
	// 		syncs.Done()
	// 	}()

	// 	// range 读取可以for i for i 不需要close range 写需要 close range channel没有key for i for range 都是安全的
	// 	for v := range ch {
	// 		fmt.Println(v)
	// 	}

	// }()
	// syncs.Wait()

	// 多协程序跑 % 3 = 0的
	// 要被统计的管道
	// numbers := make(chan int, 100)
	// // 统计的管道
	// ts := make(chan int, 100)

	// // 写入线程
	// syncs.Add(1)
	// go func(items chan<- int) {
	// 	defer func() {
	// 		close(items)
	// 		syncs.Done()
	// 	}()

	// 	for i := 0; i < 2000; i++ {
	// 		items <- i
	// 	}

	// }(numbers)

	// // 定义线程是否都跑完的管道
	// process := make(chan int, 10)

	// // 多协程消费 10 个协程跑
	// for i := 0; i < 10; i++ {
	// 	syncs.Add(1)
	// 	go func(items <-chan int) {
	// 		defer func() {
	// 			process <- 1
	// 			syncs.Done()
	// 		}()
	// 		for val := range items {
	// 			if val%3 == 0 {
	// 				ts <- val
	// 			}
	// 		}
	// 	}(numbers)
	// }

	// // 消费所有协程序都完成
	// syncs.Add(1)
	// go func(process <-chan int) {
	// 	defer func() {
	// 		fmt.Println("done")
	// 		// 关闭统计的管道
	// 		close(ts)
	// 		syncs.Done()
	// 	}()

	// 	for i := 0; i < 10; i++ {
	// 		<-process
	// 	}

	// }(process)

	// // 读取ts
	// syncs.Add(1)
	// go func(ts <-chan int) {
	// 	defer syncs.Done()
	// 	for val := range ts {
	// 		fmt.Println(val)
	// 	}
	// }(ts)

	// syncs.Wait()

	// 单向管道 做约束用的
	// d := make(chan int, 10)    // 默认双向 可度可写
	// d1 := make(chan<- int, 10) // 只能写  只能读<-chan int 否则 阻塞

	// select 多路复用 并行从管道取数
	ch1 := make(chan int, 10)
	for i := 0; i < 10; i++ {
		ch1 <- i
	}

d:
	for { // 并发执行随机通道都获取完毕了就走defalut了  多路复用写不需要close
		select { // case 可以取多个管道随机取 走case 随机的
		case d := <-ch1:
			fmt.Println(d)
		default: // 通道都光了就走这个了然后break就好
			fmt.Println("done")
			break d
		}
	}

	// 解决协程的panic 多协程有一个有问题其它的就崩了所以需要在协程里面处理下recover defer里面处理就行

	// 锁
	// 互斥锁 同时操作一个资源 go build -race main.go 查看是否有竞争关系 有问题就打印竞争关系了

	//互斥锁枷锁后一个时刻只能有一个操作 其它协程序等待
	var r sync.Mutex
	r.Lock()
	//处理程序
	fmt.Print("xx")
	r.Unlock()

	// 读写互斥锁 写的时候锁住  读的时候不锁住 写的时候互斥 读的时候并行
	// var rs sync.RWMutex
	// // 读
	// rs.RLock()
	// // 读取公共资源的代码
	// rs.Unlock()

	// // 写
	// rs.Lock()
	// rs.Unlock()

}
